
package msu.ml.data.level2;

import java.io.*;

/**
 * The Packet class represents a packet
 * of Nexrad Level II data.
 *
 * @author Reginald M Mead
 * @version 1.0
 */
public class Build9Packet extends PacketBase
{
   /**
    * Create a new empty packet
    */
   public Build9Packet()
   {
      this(null);
   }

   public Build9Packet(Level2InputStream stream)
   {
      super(stream);

      data = new byte[2300];
      fts = new byte[4];

      if(stream != null)
      {
         try
         {
            if(msgType == 1)
            {
               rayTime = stream.readInt();
               rayDate = stream.readShort();
               unamRng = stream.readShort();
               azm = stream.readShort();
               rayNum = stream.readShort();
               rayStatus = stream.readShort();
               elev = stream.readShort();
               elevNum = stream.readShort();
               reflRng = stream.readShort();
               dopRng = stream.readShort();
               reflSize = stream.readShort();
               dopSize = stream.readShort();
               numRefl = stream.readShort();
               numDop = stream.readShort();
               secNum = stream.readShort();
               sysCal = stream.readFloat();
               reflPtr = stream.readShort();
               velPtr = stream.readShort();
               spcPtr = stream.readShort();
               velRes = stream.readShort();
               volCpat = stream.readShort();
               stream.skip(8);
               refPtrp = stream.readShort();
               velPtrp = stream.readShort();
               spcPtrp = stream.readShort();
               nyqVel = stream.readShort();
               atmAtt = stream.readShort();
               minDif = stream.readShort();
               stream.skip(34);
               stream.readBytes(data, 2300);
               stream.readBytes(fts, 4);
            }
            else
            {
               stream.skip(2404);
            }
         }
         catch(IOException e)
         {
            System.out.println(e.getMessage());
         }
      }
   }

   public boolean isRadialData()
   {
      return getMessageType() == 1;
   }

   //Date Header Information

   /**
    * Ray collection time in milliseconds since midnight (UTC)
    */
   protected int rayTime;
   public int getRayTime()
   {
      return rayTime;
   }

   /**
    * Modified Julian Date since Jan 1 1970
    */
   protected short rayDate;
   public int getRayDate()
   {
      return rayDate;
   }

   /**
    * Unambiguous Range is the range at which data can start to
    * become ambiguous due to the doppler dilemna. Value / 10 = 
    * range in Kilometers
    */
   protected short unamRng;
   public int getUnambiguousRange()
   {
      return unamRng;
   }

   /**
    * Azimuth of ray. Degrees = ((Value / 8) * (180 / 4096)). 
    * 0 degrees = North, 90 degrees = east.
    */
   protected short azm;
   public double getAzimuth()
   {
      return azm / 8.0 * (180.0 / 4096.0);
   }

   /**
    * Number of ray within elevation sweep
    */
   protected short rayNum;
   public int getRayNumber()
   {
      return rayNum;
   }

   /**
    * Status of Ray
    * <ul>
    * 	<li>0 = Start of New Elevation</li>
    * 	<li>1 = Intermediate Radial</li>
    * 	<li>2 = End of Elevation</li>
    * 	<li>3 = Beginning of Volume Scan</li>
    * 	<li>4 = End of Volume Scan</li>
    * </ul>
    */
   protected short rayStatus;
   public int getRayStatus()
   {
      return rayStatus;
   }

   /**
    * Elevation Angle. Degrees = ((Value / 8) * (180 / 4096)).
    * 0 degrees = parallel to pedestal, 
    * 90 degrees = perpendicular to pedestal.
    */
   protected short elev;
   public double getElevation()
   {
      return elev / 8.0 * (180.0 / 4096.0);
   }

   /**
    * RDA elevation number within volume scan
    */
   protected short elevNum;
   public int getElevationNumber()
   {
      return elevNum;
   }

   public double getRayWidth()
   {
      return 1.0;
   }

   //Gate/Bin Information
   /**
    * Range in meters to first reflectivity gate
    */
   protected short reflRng;
   public int getReflectivityRange()
   {
      return reflRng;
   }

   /**
    * Range in meters to first doppler gate
    */
   protected short dopRng;
   public int getDopplerRange()
   {
      return dopRng;
   }

   /**
    * Reflectivity gate size in meters
    */
   protected short reflSize;
   public int getReflectivityGateSize()
   {
      return reflSize;
   }

   /**
    * Doppler gate size in meters
    */
   protected short dopSize;
   public int getDopplerGateSize()
   {
      return dopSize;
   }

   /**
    * Number of reflectivity gates
    */
   protected short numRefl;
   public int getReflectivityGates()
   {
      return numRefl;
   }

   /**
    * Number of doppler gates
    */
   protected short numDop;
   public int getDopplerGates()
   {
      return numDop;
   }

   /**
    * Sector withing cut
    */
   protected short secNum;
   public int getSectorNumber()
   {
      return secNum;
   }

   /**
    * System Gain Calibration Constance (dB biased)
    */
   protected float sysCal;
   public double getSystemCalibration()
   {
      return sysCal;
   }

   //Data Parameters
   /**
    * Data Pointer, referenced from beginning of message
    */
   public short reflPtr;
   /**
    * Data Pointer, referenced from beginning of message
    */
   public short velPtr;
   /**
    * Data Pointer, referenced from beginning of message
    */
   public short spcPtr;
   /**
    * Velocity Resolution, 2 = .5 m/s, 4 = 1 m/s.
    */
   protected short velRes;
   public int getVelocityResolution()
   {
      return velRes;
   }

   /**
    * Volume Coverage Pattern (Determines number and type of sweeps)
    */
   protected short volCpat;
   public int getVolumeCoveragePattern()
   {
      return volCpat;
   }

   //Data Pointers for Archive II Playback
   /**
    * Unused. RDA Playback Pointers
    */
   public short refPtrp;
   /**
    * Unused. RDA Playback Pointers
    */
   public short velPtrp;
   /**
    * Unused. RDA Playback Pointers
    */
   public short spcPtrp;

   /**
    * Nyquist Velocity (Value / 100) = m/s
    */
   protected short nyqVel;
   public int getNyquistVelocity()
   {
      return nyqVel;
   }

   /**
    * Attmopheric Attenuation (value / 100) = dB / KM
    */
   protected short atmAtt;
   public double getAtmosphericAttenuation()
   {
      return (double)atmAtt / 10.0f;
   }

   /**
    * Minimum Difference. Threshold parameter for minimum difference
    * in echo power between two resolution volumes for them not to be
    * labeled range ambiguous. (Value / 10) = watts
    */
   protected short minDif;
   public int getSNRThreshold()
   {  
      return minDif;
   }

   /**
    * Digital Radar Data, 0 = BADVAL, 1 = RANGE_FOLDED
    */
   public byte[] data; //unsigned
   public double [] getReflectivityData()
   {
      double [] result = new double[getReflectivityGates()];
      for(int i = 0; i < result.length; i++)
      {
         byte rawData = data[reflPtr - 100 + i];
         int unsignedData = (int)(rawData & 0xFF); 
         if(unsignedData > 1)
            result[i] = (((float)unsignedData - 2.0f) / 2.0f) - 32.0f;
         else if(unsignedData == 1)
            result[i] = Ray.RFVAL;
         else
            result[i] = Ray.BADVAL;
      }
      return result;
   }

   public double [] getVelocityData()
   {
      double [] result = new double[getDopplerGates()];
      for(int i = 0; i < result.length; i++)
      {
         byte rawData = data[velPtr - 100 + i];
         int unsignedData = (int)(rawData & 0xFF); 
         if(unsignedData > 1)
         {
            if(velRes == 2)
               result[i] = (((float)unsignedData - 2.0f) / 2.0f) - 63.5f;
            else
               result[i] = ((float)unsignedData - 2.0f) - 127.0f;
         }
         else if(unsignedData == 1)
         {
            result[i] = Ray.RFVAL;
         }
         else
         {
            result[i] = Ray.BADVAL;
         }
      }
      return result;
   }

   public double [] getSpectrumWidthData()
   {
      double [] result = new double[getDopplerGates()];
      for(int i = 0; i < result.length; i++)
      {
         byte rawData = data[spcPtr - 100 + i];
         int unsignedData = (int)(rawData & 0xFF); 
         if(unsignedData > 1)
            result[i] = (((float)unsignedData - 2.0f) / 2.0f) - 63.5f; 
         else if(unsignedData == 1)
            result[i] = Ray.RFVAL;
         else
            result[i] = Ray.BADVAL;
      }
      return result;

   }

   /**
    * Frame Check Sequence
    */
   public byte[] fts; //unsigned

   /**
    * Return a string representation of this packet.
    *
    * @return the string representation of this packet
    */
   public String toString()
   {
      String result = "Message Size: " + this.msgSize + "\n";
      result += "Message Type (Composite): " + this.msgType + "\n";
      result += "\tMessage Channel: " + ((this.msgType >> 8) & 0xFF) + "\n";
      result += "\tMessage Type: " + (this.msgType & 0xFF) + "\n";
      result += "ID Seq: " + this.idSeq + "\n";
      result += "Number of Segments: " + this.numSeg + "\n";
      result += "Segment Number: " + this.segNum + "\n";
      result += "Unambiguous Range: " + this.unamRng + "\n";
      result += "Azimuth: " + this.azm + "\n";
      result += "Ray Number: " + this.rayNum + "\n";
      result += "Ray Status: " + this.rayStatus + "\n";
      result += "Elevation: " + this.elev + "\n";
      result += "Elevation Number: " + this.elevNum + "\n";
      result += "Reflectivity Gate1 Range: " + this.reflRng + "\n";
      result += "Doppler Gate1 Range: " + this.dopRng + "\n";
      result += "Reflectivity Gate Size: " + this.reflSize + "\n";
      result += "Doppler Gate Size: " + this.dopSize + "\n";
      result += "Reflectivity Gates: " + this.numRefl + "\n";
      result += "Doppler Gates: " + this.numDop + "\n";
      result += "Sector Number: " + this.secNum + "\n";
      result += "Gain Calibration Constant: " + this.sysCal + "\n";
      result += "Reflectivity Pointer: " + ((int)(this.reflPtr)) + "\n";
      result += "Velocity Pointer: " + this.velPtr + "\n";
      result += "Spectrum Width Pointer: " + this.spcPtr + "\n";
      result += "Doppler Resolution: " + this.velRes + "\n";
      result += "Volume Coverage Pattern: " + this.volCpat + "\n";
      result += "Nyquist Velocity: " + this.nyqVel + "\n";
      result += "Atmospheric Attenuation Factor: " + this.atmAtt + "\n";
      result += "Minimum Difference Threshold: " + this.minDif;

      return result;
   }
}
